﻿
namespace Sai.Core.Rules
{
    using Sai.Core.Introspection;
    using System;
    using System.Collections.Generic;
    using System.Linq;

    internal sealed class CompareEnumByTextAndValue : IEqualityComparer<EnumValue>
    {
        public bool Equals(EnumValue x, EnumValue y)
        {
            const int ExactMatch = 0;
            return string.Compare(
                                x.Name,
                                y.Name,
                                StringComparison.OrdinalIgnoreCase)
                                == ExactMatch && x.Value == y.Value;
        }

        public int GetHashCode(EnumValue obj)
        {
            return obj.ToString().GetHashCode() ^ obj.Value.GetHashCode();
        }
    }

	internal sealed class CompareEnumByText : IEqualityComparer<EnumValue>
	{
		public bool Equals(EnumValue x, EnumValue y)
		{
			const int ExactMatch = 0;
			return string.Compare(
								x.Name,
								y.Name,
								StringComparison.OrdinalIgnoreCase)
								== ExactMatch;
		}

		public int GetHashCode(EnumValue obj)
		{
			return obj.Name.GetHashCode();
		}
	}

    internal sealed class CompareEnumByValue : IEqualityComparer<EnumValue>
    {
        public bool Equals(EnumValue x, EnumValue y)
        {
            return x.Value == y.Value;
        }

        public int GetHashCode(EnumValue obj)
        {
            return obj.Value.GetHashCode();
        }
    }

	public class CheckEnumValues : IEnumRule
	{
        public void Validate(EnumTypeDeclaration was, EnumTypeDeclaration isNow, ITypeDifference typeDifference)
		{
            // don't need to report on obsolete type
            if (isNow.IsObsolete)
                return;

			if (was != null)
			{
                // debugging
                // string fileName = "C:\\" + wasEnum.TypeName.SafeFullName + ".txt";

                //if (System.IO.File.Exists(fileName))
                //    System.IO.File.Delete(fileName);

                // enums may be the same...
                if (AreSequencesEqual(was.AllowedValues, isNow.AllowedValues))
                {
                    return;
                }

                IEnumerable<EnumValue> added = isNow.AllowedValues.Except(was.AllowedValues, new CompareEnumByTextAndValue());

                IEnumerable<EnumValue> removed = was.AllowedValues.Except(isNow.AllowedValues, new CompareEnumByTextAndValue());

                if (added.Any() || removed.Any())
                {
                    // some removed and some added - have values changed because of an insert/deletion
                    // in the middle of a sequence ?
                    var insertInSequence = added.Except(removed, new CompareEnumByText());

                    if (insertInSequence.Any())
                    {
                        foreach (var insert in insertInSequence)
                        {
                            typeDifference.IdentifiedChange(
                                new UnaryTypeChange(
                                    TypeChangeReason.ValueAdded,
                                    insert
                                    ));
                        }
                    }

                    var removeSequence = removed.Except(added, new CompareEnumByText());

                    if (removeSequence.Any())
                    {
                        foreach (var remove in removeSequence)
                        {
                            typeDifference.IdentifiedChange(
                                new UnaryTypeChange(
                                    TypeChangeReason.ValueRemoved,
                                    remove
                                    ));
                        }
                    }
                }

                //if (added.Any() || removed.Any())
                //{
                //    using (System.IO.Stream s = new System.IO.FileStream("C:\\" + wasEnum.TypeName.SafeFullName + ".txt", System.IO.FileMode.CreateNew))
                //    {
                //        using (System.IO.TextWriter writer = new System.IO.StreamWriter(s))
                //        {
                //            writer.WriteLine("Removed " + wasEnum.TypeName.FullName);

                //            foreach (EnumValue v in removed)
                //            {
                //                writer.WriteLine(v.Name + " = " + v.Value);
                //            }

                //            writer.WriteLine("Added " + isNow.TypeName.FullName);
                //            foreach (EnumValue v in added)
                //            {
                //                writer.WriteLine(v.Name + " = " + v.Value);
                //            }

                //            writer.WriteLine("=============================");

                //            foreach (EnumValue v in wasEnum.AllowedValues)
                //            {
                //                writer.WriteLine(v.Name + " = " + v.Value);
                //            }

                //            writer.WriteLine("=============================");

                //            foreach (EnumValue v in isNow.AllowedValues)
                //            {
                //                writer.WriteLine(v.Name + " = " + v.Value);
                //            }
                //        }
                //    }
                //}

            }
		}

        private bool AreSequencesEqual(IList<EnumValue> first, IList<EnumValue> second)
        {
            bool equal = first.Count == second.Count;

            if (equal)
            {
                // check individual items...
                for (int i = 0; i < first.Count; ++i)
                {
                    if (first[i] != second[i])
                    {
                        equal = false;
                        break;
                    }
                }
            }

            return equal;
        }

	}

}
